package io.summer.basis.tool;

import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Objects;

/**
 * @author ml
 */
public abstract class IpTool {
  private static final String UNKNOWN = "unknown";
  private static final String IPV6_LOCAL = "0:0:0:0:0:0:0:1";
  private static final String[] IP_HEADER_CANDIDATES = {
    "X-Forwarded-For",
    "Proxy-Client-IP",
    "WL-Proxy-Client-IP",
    "HTTP_X_FORWARDED_FOR",
    "HTTP_X_FORWARDED",
    "HTTP_X_CLUSTER_CLIENT_IP",
    "HTTP_CLIENT_IP",
    "HTTP_FORWARDED_FOR",
    "HTTP_FORWARDED",
    "HTTP_VIA",
    "REMOTE_ADDR"
  };

  static String getRequestIp() {
    RequestAttributes attributes = RequestContextHolder.getRequestAttributes();
    if (Objects.isNull(attributes)) {
      return "0.0.0.0";
    }
    HttpServletRequest request = ((ServletRequestAttributes) attributes).getRequest();
    return getRequestIp(request);
  }

  public static String getRequestIp(HttpServletRequest request) {
    String ip = null;
    for (String header : IP_HEADER_CANDIDATES) {
      String ipList = request.getHeader(header);
      if (ipList != null && ipList.length() != 0 && !UNKNOWN.equalsIgnoreCase(ipList)) {
        ip = ipList.split(",")[0];
        break;
      }
    }
    if (ip == null) {
      ip = request.getRemoteAddr();
    }
    if (IPV6_LOCAL.equals(ip)) {
      ip = getLocalhostIp();
    }
    return ip;
  }

  public static String getLocalhostIp() {
    try {
      return InetAddress.getLocalHost().getHostAddress();
    } catch (UnknownHostException ignored) {
    }
    return UNKNOWN;
  }

  /**
   * 是否是一个合法的port
   * @param port 端口
   * @return true = 是
   */
  public static boolean checkPort(int port) {
    return port >= 0 && port < 0xFFFF;
  }

  public static boolean isConnectable(String host, int port) {
    try (Socket socket = new Socket()) {
      InetSocketAddress address = new InetSocketAddress(host, port);
      if (address.isUnresolved()) {
        return false;
      }
      socket.connect(address);
    } catch (IOException e) {
      return false;
    }
    return true;
  }
}
